Codes
Code your ideas for understanding of natural systems
Updated at 2021.6.10
Blazor and Image File
Web App에서 사용자 컴퓨터에 있는 이미지 파일을 선택하여 웹에 보여주고, 필요시 서버에 업로드할 필요가 있다. 여기서는 Blazor에서 파일을 선택하고 Data Uri
를 이용하여 이미지를 보여주는 기능을 구현해 볼 것이다.
다음 3가지에 대한 지식이 필요하다.
파일 선택 및 로딩 하기
InputFile Component
파일 선택 창을 보여주고 로컬 위치에서 파일을 선택할 때 그 파일에 대한 정보로 반환하는 컴포넌트이다. 기본적으로 HTML에서의 <input>
요소를 렌더링한 것이다.
<input type="file" multiple>
일반적인 사용법은 다음과 같다.
<InputFile OnChange="@LoadFiles" multiple />
@code {
private void LoadFiles(InputFileChangeEventArgs e)
{
...
}
}
OnChange
이벤트에서 사용되는 메서드는 InputFileChangeEventArgs
파라미터를 포함하고 있는데, 이 클래스는 다음과 같은 프로퍼티와 메서드를 가지고 있다.
- Properties
File
:IBrowserFile
인테페이스 타입이다.
- Methods
- IReadOnlyList
GetMultipleFiles
(int maximumFileCount):multiple
옵션 선택시IBrowserFile
리스트를 반환하는 함수이다.
- IReadOnlyList
위 둘을 활용하여 파일 브라우저에서 선택된 파일의 정보를 가지고 있는 IBrowserFile
를 얻을 수 있다.
IBrowserFile Interface
InputFile
컴포넌트에서 선택된 파일에 대한 정보를 가지고 있는 인터페이스로서 다음과 같은 속성과 메소드를 통해 파일 정보를 얻거나 스트림으로 읽어 오고, 이미지 파일의 형식 및 사이즈를 변경할 수 있다.
- Properties
ContentType
: MIME 타입LastModified
Name
Size
: 몇 바이트인가?
- Methods
- Stream
OpenReadStream
(long maxAllowedSize, CancellationToken cancellationToken)
- Stream
- Extension Methods
- ValueTask
RequestImageFileAsync
(this IBrowserFile browserFile, string format, int maxWidth, int maxHeight)
- ValueTask
위의 두 메서드에 대한 사용 예는 다음과 같다.
private void LoadFiles(InputFileChangeEventArgs e)
{
...
foreach (var file in e.GetMultipleFiles(MaxAllowedFiles))
{
string format = "image/jpeg";
IBrowserFile imageFile = await file.RequestImageFileAsync(format, 980, 980);
Stream fileStream = imageFile.OpenReadStream(maxFileSize);
}
...
}
이미지 데이터를 Uri로
우선 Uri과 Url의 차이에 대해 알아보자.
- Uri: Uniform Resource Identifier (식별자)
- Url: Uniform Resource Locator (위치), 인터넷에서 웹페이지, 이미지, 비디오 등 리스스의 위치를 나타내는 문자열이다.
Uri
Uri의 일반적인 구조는 다음과 같다.
scheme:[//[user[:password]@]host[:port]][/path][?query][#fragment]
scheme
: 프로토콜, 웹에서는 일반적으로http
또는https
이다.user
,passwd
: 인증된 사용자만 접속할 경우의 이름과 비밀번호이다.host
,port
: 접속할 서버의 호스트 이름과 포트 번호이다.path
: 접속한 서버 내에서의 상세 경로이다.query
: 요청할 정보에 대한 추가적인 파라미터로 사용된다fragment
: 요청한 정보 내에서의 추가적인 위치를 나타낸다.
Data Uri
data:
스킴을 활용하여 컨텐츠를 문서 내에 인라인으로 포함시킬 수 있는 방법이다. 기본 구문은 아래와 같다.
data:[<mediatype>][;base64],<data>
mediatype
은 MIME 타입으로 JPEG 이미지는image/jpeg
이다. 생략도 가능한데, 생략하면 기본값은text/plain;charset=US-ASCII
이다.- 텍스트가 아니면
base64
로 인코딩된 이진데이터를 덧붙이면 된다. - 몇가지 예제
- data:,Hello%2C%20World! (단순 텍스트 포함시, 컴마를 빼먹으면 안됨)
- data:text/plain;base64,xxx (MIME 타입 명시하고, 데이터 포멧이 이진화 된 것일 때)
적용하기
우리가 구현하고자 하는 것은 이미지 파일을 이진화 시켜서 웹앱에 인라인으로 포함시키고자 하는 것이어서 다음과 같이 MemoryStream
으로 읽어 들인 데이터를 Uri로 만든다.
...
string format = "image/jpeg";
MemoryStream memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
string url = $"data:{format};base64,{Convert.ToBase64String(memoryStream.ToArray())}";
Full Source Code
실제 실행가능한 소스 코드 및 실행 화면은 다음과 같다.
ImagePreview.razor
<p>
<InputFile OnChange="LoadImages" id="input-image" accept="image/png,image/gif,image/jpeg" multiple />
<span id="exception-message">@exceptionMessage</span>
</p>
@if (isLoading)
{
<p>Loading...</p>
}
@foreach (var (file, content) in loadedFiles)
{
<p id="file-@(file.Name)">
<strong>Name:</strong> <span id="file-name">@(file.Name)</span><br />
<strong>Last modified:</strong> <span id="file-last-modified">@(file.LastModified.ToString())</span><br />
<strong>Size (bytes):</strong> <span id="file-size">@(file.Size)</span><br />
<strong>Content type:</strong> <span id="file-content-type">@(file.ContentType)</span><br />
<img id="image-uploaded" src="@content" /><br />
</p>
}
@using System.IO;
@code {
[Parameter]
public int MaxAllowedFiles { get; set; } = 5;
Dictionary<IBrowserFile, string> loadedFiles = new Dictionary<IBrowserFile, string>();
long maxFileSize = 1024 * 1024 * 15;
bool isLoading;
string exceptionMessage;
async Task LoadImages(InputFileChangeEventArgs e)
{
isLoading = true;
loadedFiles.Clear();
exceptionMessage = string.Empty;
try
{
foreach (var file in e.GetMultipleFiles(MaxAllowedFiles))
{
StateHasChanged();
loadedFiles.Add(file, await LoadImage(file));
}
}
catch (Exception ex)
{
exceptionMessage = ex.Message;
}
isLoading = false;
}
async Task<string> LoadImage(IBrowserFile file)
{
var format = "image/jpeg";
var imageFile = await file.RequestImageFileAsync(format, 980, 980);
using var fileStream = imageFile.OpenReadStream(maxFileSize);
using var memoryStream = new MemoryStream();
await fileStream.CopyToAsync(memoryStream);
return $"data:{format};base64,{Convert.ToBase64String(memoryStream.ToArray())}";
}
}
총 17 개의 글이 있습니다.
# | 제목 | 날짜 | 조회수 |
---|---|---|---|
01 | CS 배우기 요약 | 2021/06/07 | 146 |
02 | CS Statements | 2021/06/07 | 129 |
03 | 퍼셉트론 | 2021/04/15 | 125 |
04 | Blazor and Sqlite | 2021/04/15 | 138 |
05 | Blazor Layouts | 2021/04/15 | 161 |
06 | CS Language Reference | 2021/06/07 | 127 |
07 | VSCode and Markdown | 2021/04/15 | 138 |
08 | Blazor에서 이미지파일 다루기 | 2021/06/10 | 212 |
09 | Blazor and Markdown | 2021/04/15 | 145 |
10 | 종속성 주입 | 2021/06/07 | 153 |
11 | Blazor에서 데이터 다루기 | 2021/06/07 | 138 |
12 | Blazor Components | 2021/04/15 | 148 |
13 | CS Glossary | 2021/06/07 | 127 |
14 | Enum 타입 다루기 | 2021/12/14 | 135 |
15 | 생활코딩 CS01 | 2022/04/25 | 261 |
16 | 생활코딩 CS02 | 2022/04/30 | 165 |
17 | 생활코딩 CS03 | 2022/04/30 | 441 |